<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <link rel="icon" type="image/svg+xml" href="/vite.svg"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>fileToBase64</title>
</head>
<body>
<input type="file" name="" id="file"/>
<script>
    const ele = document.querySelector("#file");
    ele.addEventListener("change", async (e) => {
        const file = e.target.files[0];
        // const base64 = await fileToBase64(file)
        // const base64 = await fileCanvasToBase64(file)
        /*---模拟url转blob---*/
        const url = window.URL.createObjectURL(file)
        const blob = await urlToBlob(url)
        const base64 = await fileToBase64(blob)
        /*---end---*/
        const resBlob = await base64ToBlob(base64.split(',')[1])
        console.log(resBlob)
        const r = await fileToBase64(resBlob)
        console.log(r);
    });

    /**
     * file或blob转base64
     * @param {Blob|File} blob
     * @return {Promise} base64
     * */
    function fileToBase64(blob) {
        return new Promise((resolve, reject) => {
            const fileReader = new FileReader();
            /*
             * readAsDataURL读取指定的 Blob 或 File 对象。读取操作完成的时候，readyState 会变成已完成DONE，并触发 loadend 事件,
             * 同时 result 属性将包含一个data:URL 格式的字符串（base64 编码）以表示所读取文件的内容
             * */
            fileReader.readAsDataURL(blob);
            fileReader.onload = (res) => {
                resolve(res.target.result);
            };
            fileReader.onerror = (err) => {
                reject(err);
            };
        });
    }

    /**
     * file通过canvas转base64 (可指定图片后缀、压缩图片)
     * @param {Blob|File} blob
     * @return {Promise}
     * */
    function fileCanvasToBase64(blob) {
        return new Promise((resolve, reject) => {
            const imgUrl = window.URL.createObjectURL(blob)
            const img = new Image()
            img.src = imgUrl
            img.onload = () => {
                const canvas = document.createElement('canvas')
                const ctx = canvas.getContext('2d')
                canvas.width = img.width
                canvas.height = img.height
                ctx.drawImage(img, 0, 0) // 参数：图片的源路径，以及图片应该在canvas上显示的起始坐标和结束坐标
                const base64 = canvas.toDataURL('image/webp', 0.3) // type参数1:图片格式，默认为 image/png,参数2：image/jpeg或image/webp可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围，将会使用默认值 0.92。其他参数会被忽略
                resolve(base64)
            }
            img.onerror = (err) => reject(err)
        });
    }

    /**
     * url转blob
     * @param {String} url
     * @return {Blob}
     */
    function urlToBlob(url) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest()
            xhr.open('GET', url)
            xhr.responseType = "blob"
            xhr.onreadystatechange = () => {
                const {status, response} = xhr
                if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
                    resolve(response)
                    return;
                }
                if (xhr.status > 299 || xhr.status < 199) {
                    reject({status, response})
                }
            }
            xhr.send()
        })
    }

    /**
     * base64转blob
     * @param {String} base64 不包含 data:image/png;base64,
     * @return {Promise} blob
     * */
    function base64ToBlob(base64) {
        return new Promise((resolve, reject) => {
            if (!base64 || typeof base64 !== 'string') {
                reject('base64有误')
                return;
            }
            const binary = atob(base64)
            const arr = []
            for (let i = 0; i < binary.length; i++) {
                arr.push(binary.charCodeAt(i)) // Unicode 值
            }
            const blob = new Blob([new Uint8Array(arr)], {type: 'image/jpeg'}) // 若base64图片格式为png、webp也能显示
            resolve(blob)
        })
    }

    /**
     * file转二进制流
     * @param {File} file
     * @return {Promise} arrayBuffer
     * */
    function fileToArrayBuffer(file){
        new Promise((resolve, reject)=>{
            const render = new FileReader()
            render.readAsArrayBuffer(file)
            // axios headers: {'Content-Type': 'application/octet-stream'} // 指定内容类型为二进制流
            render.onload=()=>{
                resolve(render.result)
            }
            render.onerror=()=>{
                reject('file转二进制流失败！')
            }
        })
    }

</script>
</body>
</html>
